// (c) Copyright 2013 Mike Riedel.
// This source is subject to the Microsoft Public License (Ms-PL).
// All other rights reserved.

using System;

namespace FreshMeat.ErrorHandlingBlock
{
   /// <summary>
   /// Simple error policy.
   /// </summary>
   public class ErrorPolicy : IErrorPolicy
   {
      readonly IErrorDetector detector;
      readonly IRetryPlan plan;

      /// <summary>
      /// Creates instance using <see cref="DefaultErrorDetector"/> and <see cref="NoRetryPlan"/>.
      /// The net effect is that all exceptions are thrown unchanged and with no retries.
      /// </summary>
      public ErrorPolicy()
         : this(new DefaultErrorDetector(), new NoRetryPlan())
      {
      }

      /// <summary>
      /// Creates instance using <see cref="DefaultErrorDetector"/> and the specified plan.
      /// </summary>
      /// <param name="plan">Retry plan.</param>
      public ErrorPolicy(IRetryPlan plan)
         : this(new DefaultErrorDetector(), plan)
      {
      }

      /// <summary>
      /// Creates instance using specified parameters.
      /// </summary>
      /// <param name="detector">Error detector.</param>
      /// <param name="plan">Retry plan.</param>
      public ErrorPolicy(IErrorDetector detector, IRetryPlan plan)
      {
         this.detector = detector;
         this.plan = plan;
      }

      /// <summary>
      /// Determines whether the policy applies to an error.
      /// </summary>
      /// <param name="error">The error.</param>
      /// <returns>True if this policy applies to the error.</returns>
      public bool IsApplicable(Exception error)
      {
         return detector.IsRelevant(error);
      }

      /// <summary>
      /// Applies the policy to an error.
      /// </summary>
      /// <param name="error">The error.</param>
      /// <param name="attempts">How many times the error has occurred.</param>
      /// <returns>The result of applying the policy, which includes a diagnosis of the problem and whether to retry.</returns>
      public ErrorPolicyResult Apply(Exception error, int attempts)
      {
         var diagnosis = detector.Diagnose(error);

         if (!plan.ShouldRetry(attempts))
            return new ErrorPolicyResult(diagnosis); // no retry
         
         var wait = plan.GetTimeUntilNextAttempt(attempts);
         return new ErrorPolicyResult(diagnosis, wait); // retry
      }
   }
}